#include "Model/Calendar.hpp"
#include "Model/OneOccurenceRule.hpp"
#include "CalendarRuleTests.hpp"
#include <boost/test/auto_unit_test.hpp>

namespace
{

using namespace Model;

/// \brief It provides the calendar test data.
/// \details
///		It sets a calendar with the following rules: \n
///		\li OneTimeOccurenceRule: times_[2], times_[5]
///		\li OneTimeOccurenceRule: times_[8], times_[11]
class TestFixture
{
public:
	TestFixture()
		: calendar_( Calendar::Time( Calendar::Date( 2008, 12, 1)), Calendar::Time( Calendar::Date( 2008, 12, 13)))
	{
		times_.reserve( 14);
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 1)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 2)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 3)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 4)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 5)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 6)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 7)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 8)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 9)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 10)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 11)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 12)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 13)));
		times_.push_back( Calendar::Time( Calendar::Date( 2008, 12, 14)));

		calendar_.addRule( new OneOccurenceRule( times_[2], times_[4]));
		calendar_.addRule( new OneOccurenceRule( times_[8], times_[11]));
	}

public:
	std::vector< Calendar::Time> times_;
	Calendar calendar_;
};


BOOST_AUTO_TEST_CASE( testDistribution01)
{
	// 0 1 2 3 4 5 6 7 8 9 10 11 12
	//     |===|       |=======|
	// |^|
	TestFixture fixture;
	Calendar::PeriodSet result;
	fixture.calendar_.distributeDuration( fixture.times_[0], Calendar::Duration( 24, 0, 0), result);

	BOOST_REQUIRE_EQUAL( 1, result.size());
	CHECK_TIME_INTERVAL( fixture.times_[0], fixture.times_[1], *result.begin());
}

// ---------------------------------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE( testDistribution02)
{
	// 0 1 2 3 4 5 6 7 8 9 10 11 12
	//     |===|       |=======|
	// |^^^|   |^^^|
	TestFixture fixture;
	Calendar::PeriodSet result;
	fixture.calendar_.distributeDuration( fixture.times_[0], Calendar::Duration( 96, 0, 0), result);

	BOOST_REQUIRE_EQUAL( 2, result.size());
	Calendar::PeriodSet::const_iterator resI = result.begin();
	CHECK_TIME_INTERVAL( fixture.times_[0], fixture.times_[2], *resI); ++resI;
	CHECK_TIME_INTERVAL( fixture.times_[4], fixture.times_[6], *resI);
}

// ---------------------------------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE( testDistribution03)
{
	// 0 1 2 3 4 5 6 7 8 9 10 11 12
	//     |===|       |=======|
	// |^^^|   |^|
	TestFixture fixture;
	Calendar::PeriodSet result;
	fixture.calendar_.distributeDuration( fixture.times_[0], Calendar::Duration( 72, 0, 0), result);

	BOOST_REQUIRE_EQUAL( 2, result.size());
	Calendar::PeriodSet::const_iterator resI = result.begin();
	CHECK_TIME_INTERVAL( fixture.times_[0], fixture.times_[2], *resI); ++resI;
	CHECK_TIME_INTERVAL( fixture.times_[4], fixture.times_[5], *resI);
}

// ---------------------------------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE( testDistribution04)
{
	// 0 1 2 3 4 5 6 7 8 9 10 11 12
	//     |===|       |=======|
	//         |^^^^^^^|       |^^|
	// start:^
	TestFixture fixture;
	Calendar::PeriodSet result;
	fixture.calendar_.distributeDuration( fixture.times_[3], Calendar::Duration( 120, 0, 0), result);

	BOOST_REQUIRE_EQUAL( 2, result.size());
	Calendar::PeriodSet::const_iterator resI = result.begin();
	CHECK_TIME_INTERVAL( fixture.times_[4], fixture.times_[8], *resI); ++resI;
	CHECK_TIME_INTERVAL( fixture.times_[11], fixture.times_[12], *resI);
}

// ---------------------------------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE( testDistribution05)
{
	// 0 1 2 3 4 5 6 7 8 9 10 11 12
	//     |===|       |=======|
	//         |^|
	// start:^
	TestFixture fixture;
	Calendar::PeriodSet result;
	fixture.calendar_.distributeDuration( fixture.times_[3], Calendar::Duration( 24, 0, 0), result);

	BOOST_REQUIRE_EQUAL( 1, result.size());
	CHECK_TIME_INTERVAL( fixture.times_[4], fixture.times_[5], *result.begin());
}

// ---------------------------------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE( testDistribution06)
{
	// 0 1 2 3 4 5 6 7 8 9 10 11 12
	//     |===|       |=======|
	//           |^^^|
	TestFixture fixture;
	Calendar::PeriodSet result;
	fixture.calendar_.distributeDuration( fixture.times_[5], Calendar::Duration( 48, 0, 0), result);

	BOOST_REQUIRE_EQUAL( 1, result.size());
	CHECK_TIME_INTERVAL( fixture.times_[5], fixture.times_[7], *result.begin());
}

// ---------------------------------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE( testDistribution07)
{
	// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
	//     |===|       |=======|
	//                            |^^|
	TestFixture fixture;
	Calendar::PeriodSet result;
	fixture.calendar_.distributeDuration( fixture.times_[12], Calendar::Duration( 24, 0, 0), result);

	BOOST_REQUIRE_EQUAL( 1, result.size());
	CHECK_TIME_INTERVAL( fixture.times_[12], fixture.times_[13], *result.begin());
}

// ---------------------------------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE( testDistribution08)
{
	// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
	//     |===|       |=======|
	//         |^^^^^^^|
	TestFixture fixture;
	Calendar::PeriodSet result;
	fixture.calendar_.distributeDuration( fixture.times_[4], Calendar::Duration( 96, 0, 0), result);

	BOOST_REQUIRE_EQUAL( 1, result.size());
	CHECK_TIME_INTERVAL( fixture.times_[4], fixture.times_[8], *result.begin());
}

} // namespace
